<template>
  <div class="xml-path">
    <el-row :gutter="20">
      <el-col :span="12">
        <editor
          ref="aceEditor"
          v-model="content"
          width="100%"
          height="640"
          lang="xml"
          :theme="theme"
          :options="{
            enableBasicAutocompletion: true, //启用基本自动完成
            enableSnippets: true, // 启用代码段
            enableLiveAutocompletion: true, //启用实时自动完成
            tabSize: 2, //制表符大小
            fontSize: 16, //设置字号
            showPrintMargin:false  //去除编辑器里的竖线
          }"
          @init="editorInit"
          @input="editorInput"
        ></editor>
      </el-col>
      <el-col :span="12">
        <div style="width: 100%">
          <el-input placeholder="自动生成XPath" v-model="pathText" style="font-size: 16px;">
            <template slot="prepend">XPath</template>
            <template slot="append">
              <!-- <el-button 
                v-clipboard:copy="pathText"
                v-clipboard:success="firstCopySuccess"
                v-clipboard:error="firstCopyError"
              >复制</el-button> -->
              <el-button @click="onCopy">复制</el-button>
            </template>
          </el-input>
        </div>
        <div class="xml-path-parser">
          <div class="error-message" v-if="error">{{ errorMessage }}</div>
          <template v-else>
            <xml-tree :data="parserArray"></xml-tree>
          </template>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import beautify from 'beautify'
import { xmlData } from '../jsondemo/content'
import xmlTree from './xmlTree.vue'
import bus from '@/utils/bus'
export default {
  name: 'XMLPath',
  components: {
    editor: require('vue2-ace-editor'),
    xmlTree
  },
  data() {
    return {
      content: '',
      theme: 'tomorrow_night',
      error: false,
      errorMessage: '',
      dom: {},
      parserArray: [],
      pathText: '',
      domparser: null
    };
  },
  watch: {
    content: {
      handler(val) {
        const xml = beautify(val, { format: 'xml' })
        this.content = xml
      }
    }
  },
  created() {
    this.domparser = new DOMParser()
    this.content = xmlData
    bus.$on('getXMLPath', this.getXMLPath)
  },
  mounted() {
    this.editorInit()
  },

  methods: {
    getXMLPath(xpath) {
      this.pathText = xpath
    },
    onCopy() {
      this.$copyText(this.pathText).then(e=>{
        this.$message.success('复制成功')
      }, e=>{
        this.$message.error('复制失败')
      })
    },
    editorInit() {
      require("brace/ext/language_tools")
      require("brace/mode/xml")
      require("brace/theme/tomorrow_night")
      require("brace/snippets/xml")
    },
    editorInput(val) {
      this.parserXML(val)
    },
    parserXML(val) {
      this.parserArray = []
      try {
        // 解析XMLDocument对象
        this.dom = this.domparser.parseFromString(val, 'application/xml')
        // 生成XMLJavaScript对象
        this.xmlPath(this.parserArray, this.dom.documentElement)
        // 设置标签的序号
        this.setTagIndex(this.parserArray)
        // 设置元素的xpath
        this.setXPath(this.parserArray, '')
        this.error = false
        this.errorMessage = ''
      } catch(e) {
        this.error = true
        this.errorMessage = e.message
      }
    },
    xmlPath(arr, dom) {
      /* 元素对象
          {
            tagName: '', // 标签名
            index: 0, // 标签序号
            nodeType: 1, // 标签类型：1元素，2属性，3文本，8注释，9文档
            data: '', // 文本标签的值
            xpath: '', // xml path
            leaf: true, // 是否有子节点
            children: [], // 子节点对象（同当前元素类型的结构一致）
            attributes: [ // 标签属性
              { name: '', value: '', path: ''}
            ]
          }
       */

      const domItem = {
        tagName: '',
        index: 0,
        nodeType: dom.nodeType,
        data: '',
        xpath: '',
        leaf: false,
        children: [],
        attributes: []
      }

      if (domItem.nodeType === 1) {
        // 元素对象
        domItem.tagName = dom.tagName
        const attrs = dom.attributes
        if (attrs.length > 0) {
          for(let i = 0; i< attrs.length; i++) {
            const attr = {
              name: attrs[i].name,
              value: attrs[i].value,
              xpath: ''
            }
            domItem.attributes.push(attr)
          }
        }
        const childNodes = dom.childNodes
        if (childNodes.length > 0) {
          for(let i = 0; i < childNodes.length; i++) {
            const child = childNodes[i]
            if ((child.nodeType === 3 && child.data.trim() === '') || child.nodeType === 8) continue
            domItem.leaf = true
            this.xmlPath(domItem.children, child, domItem.xpath)
          }
        }
      } else if (domItem.nodeType === 3) {
        // 文本对象
        const data = dom.data.trim()
        if (data !== '') {
          domItem.data = data
        }
      }
      arr.push(domItem)
    },
    setTagIndex(arr) {
      // 设置标签序号
      if (arr.length === 1 && !arr[0].leaf) return
      arr.forEach((item, index) => {
        let count = [item]
        if (item.index === 0) {
          for (let i = 0; i < arr.length; i++) {
            if (item.tagName === arr[i].tagName && item.xpath === arr[i].xpath && index !== i) {
              count.push(arr[i])
            }
          }
        }
        if (count.length > 1) {
          count.forEach((el,n) => {
            el.index = n + 1
          })
        }
        if (item.leaf) {
          this.setTagIndex(item.children)
        }
      })
    },
    setXPath(arr, basePath) {
      arr.forEach(item => {
        if (item.nodeType === 1) {
          item.xpath = item.index === 0 ? `${basePath}/${item.tagName}` : `${basePath}/${item.tagName}[${item.index}]`
        } else if (item.nodeType === 3) {
          item.xpath = item.index === 0 ? `${basePath}/text()` : `${basePath}/text()[${item.index}]`
        }
        if (item.attributes.length > 0) {
          item.attributes.forEach(attr => {
            attr.xpath = `${item.xpath}[@${attr.name}="${attr.value}"]`
          })
        }
        if (item.leaf) {
          this.setXPath(item.children, item.xpath)
        }
      })
    }
  },
};
</script>

<style lang="scss" scoped>
.xml-path {
  box-sizing: border-box;
  padding: 10px 20px;
  .el-row {
    height: 600px;

    .el-col {
      text-align: left;
      height: 100%;

      .xml-path-parser {
        border: 1px solid #cacaca;
        box-shadow: 3px 3px 4px rgba($color: #000000, $alpha: 0.1);
        border-radius: 3px;
        height: 100%;
        width: 100%;
        box-sizing: border-box;
        padding: 10px 20px;
        overflow-y: scroll;

        .error-message {
          font-size: 16px;
          color: red;
        }
      }
    }
  }
}
</style>